#include "fxcc/core/graph/directX11/MeshJack.h"
#include "fxcc/core/graph/directX11/RenderStates.h"

using namespace fxcc::graph;
using namespace fxcc::graph::directX11;

fxcc::graph::directX11::MeshJack::MeshJack(const common::MeshJack::Desc& desc) :common::MeshJack(desc)
{
	m_Available = Init();
};

bool fxcc::graph::directX11::MeshJack::SetVertexBuffer(ID3D11Buffer** buffer, const void* data, unsigned long dataSize)
{
	auto device = directX11::RenderStates::Inst().Device();

	D3D11_BUFFER_DESC bufferDesc = {};
	bufferDesc.Usage = D3D11_USAGE_DEFAULT;
	bufferDesc.ByteWidth = dataSize;
	bufferDesc.BindFlags = D3D11_BIND_VERTEX_BUFFER;
	bufferDesc.CPUAccessFlags = 0;

	D3D11_SUBRESOURCE_DATA initData = {};
	initData.pSysMem = data;
	HRESULT hr = device->CreateBuffer(&bufferDesc, &initData, buffer);
	if (FAILED(hr))
	{
		return false;
	}
	return true;
}

bool fxcc::graph::directX11::MeshJack::Init()
{
	auto device = directX11::RenderStates::Inst().Device();
	
	const auto& geoData = m_Desc.m_GeoData;
	const auto& skinnedMeshData = m_Desc.m_SkinnedMeshData;

	if (m_Desc.HasFixedData())
	{
		m_FixedVertexBuffers.resize(5);

		FXCC_FAILED(SetVertexBuffer(m_FixedVertexBuffers[0].GetAddressOf(), geoData.vertices.data(), geoData.vertices.size() * sizeof(glm::vec3)));
		FXCC_FAILED(SetVertexBuffer(m_FixedVertexBuffers[1].GetAddressOf(), geoData.normals.data(), geoData.normals.size() * sizeof(glm::vec3)));
		FXCC_FAILED(SetVertexBuffer(m_FixedVertexBuffers[2].GetAddressOf(), geoData.texcoords.data(), geoData.texcoords.size() * sizeof(glm::vec2)));
		FXCC_FAILED(SetVertexBuffer(m_FixedVertexBuffers[3].GetAddressOf(), geoData.tangents.data(), geoData.tangents.size() * sizeof(glm::vec3)));
		FXCC_FAILED(SetVertexBuffer(m_FixedVertexBuffers[4].GetAddressOf(), geoData.bigTangents.data(), geoData.bigTangents.size() * sizeof(glm::vec3)));
	}
	
	if (m_Desc.HasSkinnedData())
	{
		m_SkinnedVertexBuffers.resize(4);

		FXCC_FAILED(SetVertexBuffer(m_SkinnedVertexBuffers[0].GetAddressOf(), skinnedMeshData.bones1s.data(), skinnedMeshData.bones1s.size() * sizeof(glm::ivec4)));
		FXCC_FAILED(SetVertexBuffer(m_SkinnedVertexBuffers[1].GetAddressOf(), skinnedMeshData.weights1s.data(), skinnedMeshData.weights1s.size() * sizeof(glm::vec4)));
		FXCC_FAILED(SetVertexBuffer(m_SkinnedVertexBuffers[2].GetAddressOf(), skinnedMeshData.bones2s.data(), skinnedMeshData.bones2s.size() * sizeof(glm::ivec4)));
		FXCC_FAILED(SetVertexBuffer(m_SkinnedVertexBuffers[3].GetAddressOf(), skinnedMeshData.weights2s.data(), skinnedMeshData.weights2s.size() * sizeof(glm::vec4)));
	};

	{
		D3D11_BUFFER_DESC indexBufferDesc = {};
		indexBufferDesc.Usage = D3D11_USAGE_DEFAULT;
		indexBufferDesc.ByteWidth = geoData.indices32.size() * sizeof(uint32_t);
		indexBufferDesc.BindFlags = D3D11_BIND_INDEX_BUFFER;
		indexBufferDesc.CPUAccessFlags = 0;

		D3D11_SUBRESOURCE_DATA indexInitData = {};
		indexInitData.pSysMem = geoData.indices32.data();

		HRESULT hr = device->CreateBuffer(&indexBufferDesc, &indexInitData, m_IndexBuffer.GetAddressOf());
		if (FAILED(hr))
		{
			return false;
		}
	}

	if (m_Desc.HasSkinnedData())
	{
		m_VertexType = fxcc::graph::common::VertexType::_vertex_skinned_;
	}
	else
	{
		m_VertexType = fxcc::graph::common::VertexType::_vertex_fixed_;
	}

	m_IndexType = fxcc::graph::common::IndexType::_index_unsigned_int_;
	m_ModeType = fxcc::graph::common::ModeType::_mode_Triangles_;

	m_NumVertices = m_Desc.m_GeoData.vertices.size();
	m_NumIndices = m_Desc.m_GeoData.indices32.size();

	return true;
}


void fxcc::graph::directX11::MeshJack::Bind() const
{
	auto context = fxcc::graph::directX11::RenderStates::Inst().Context();

	const auto& vertexDesc = directX11::RenderStates::Inst().GetVertexDesc(m_VertexType);
	const auto& indexDesc = directX11::RenderStates::Inst().GetIndexDesc(m_IndexType);
	const auto& modeDesc = directX11::RenderStates::Inst().GetModeTypeDesc(m_ModeType);

	unsigned int offset = 0;

	context->IASetPrimitiveTopology(modeDesc.m_Topology);

	static unsigned int m_FixedVertexStrides[5] =
	{
		sizeof(glm::vec3),
		sizeof(glm::vec3),
		sizeof(glm::vec2),
		sizeof(glm::vec3),
		sizeof(glm::vec3)
	};
	static unsigned int m_SkinnedVertexStrides[4] =
	{
		sizeof(glm::ivec4),
		sizeof(glm::vec4),
		sizeof(glm::ivec4),
		sizeof(glm::vec4),
	};

	for (int i = 0; i < 5; i++)
	{
		context->IASetVertexBuffers(i, 1, m_FixedVertexBuffers[i].GetAddressOf(), &m_FixedVertexStrides[i], &offset);
	}

	if (m_Desc.HasSkinnedData())
	{
		for (int i = 0; i < 4; i++)
		{
			context->IASetVertexBuffers(i + 5, 1, m_SkinnedVertexBuffers[i].GetAddressOf(), &m_SkinnedVertexStrides[i], &offset);
		}
	}
	context->IASetIndexBuffer(m_IndexBuffer.Get(), indexDesc.m_dxgiFormat, 0);
}

void MeshJack::DrawElementsAuto() const
{
	auto context = fxcc::graph::directX11::RenderStates::Inst().Context();
	context->DrawIndexed(m_NumIndices, 0, 0);
}